#include <cstdio>
#include <vector>
#include "FredkinCell.h"
#include "ConwayCell.h"

using namespace std;

template <typename T>
class Cell{
	
	private:

	/**
	* @param p AbstractCell* pointing to the cell contained by the instance of this Cell wrapper
	*/
	T* p;

	public:
	
	/**
 	* Defalut Constructor
 	*/
	Cell(){
		p=new FredkinCell();
	}
	
	/**
 	* copy constructor
 	* @param c Cell to copy
 	*/
	Cell(const Cell<T>& c){
		if(c.p->getType())
			p=new FredkinCell(c.p->getAge());
		else
			p=new ConwayCell(c.p->isAlive());
			
	}
		
	/**
 	* Destructor.
 	*/
	~Cell(){
		delete p;
	}
	
	/**
 	* returns whether the cell is alive or not
	* @return true if alive, false is dead
 	*/
	bool isAlive(){
		return p->isAlive();
	}
	
	/**
 	* evolves the cell at (x,y) given the alive neighbor count in neighbors
 	* @param neighbors alive neighbor count grids for both cell types
	* @param x x coordinate of cell
	* @param y y coordinate of cell
	* @return false always.
 	*/
	bool evolve(vector<vector<vector<int> > >& neighbors, int x, int y){
		if(p->evolve(neighbors, x, y)){//if further processing is needed
			if(p->getType()){
				if(p->getAge()==2){
					delete p;
					p=new ConwayCell(true);
				}
			}
			else{//else dead Conway cell that needs to be processed
				
				delete p;
				p=new FredkinCell();
			}	
		}
		return false;
	}

	/**
 	* increments live neighbor count around cell (X,Y). Cell must be alive for this to make sense.
 	* @param neighbors alive neighbor count for both cell types
	* @param X x coordinate of cell
	* @param Y y coordinate of cell
 	*/

	//TODO Method isn't elegant....
	void updateNeighborCount(vector<vector<vector<int> > >& neighbors, int X, int Y){
		vector<vector<int> >* n = &neighbors[0];
		//Conway neighbors
		for(int x=X-1; x <= X+1; ++x){
			for(int y=Y-1; y <= Y+1; ++y){
				++((*n)[x][y]);
			}
		}
		
		--((*n)[X][Y]);//can't increment cell's own location
		//update fredkin neighbors
		n = &neighbors[1];
		++((*n)[X+1][Y]);
		++((*n)[X][Y+1]);
		++((*n)[X-1][Y]);
		++((*n)[X][Y-1]);
	}
	
	/**
 	* prints the cell
 	*/
	void print(){
		p->print();
	}
	
	/**
 	* set the state of the cell
	* @param c char that represents the cell's state
 	*/
	void setCell(char c){
		if(c!='*' && c!='.')
			p->setCell(c);
		else{
			delete p;
			p=new ConwayCell();
			p->setCell(c);
		}
	}
	
	/**
 	* resets the live neighbor counts for both cell type neighbor grids
 	* @param neighbors alive neighbor count.
 	*/
	//TODO this could probably be optimized a little more
	static void resetNeighborCount(vector<vector<vector<int> > >& neighbors){
		ConwayCell::resetNeighborCount(neighbors);
		FredkinCell::resetNeighborCount(neighbors);
	}

};

